/**
 * 大数据加减乘除
 *  //加法
 * 	var a = '9761739471947993179847192374917249712934791274917239471924';
	var b = '2717493719479173491794719247913419734917349172947319479174912794719';
	console.log("add:" + this.add(a, b));
	//减法
	var a = '-2717493719479173491794719247913419734917349172947319479174912794719';
	var b = '9761739471947993179847192374917249712934791274917239471924';
	console.log("sub:" + this.sub(a, b));
	//乘法
	var a = '2717493729240912963742712427760612109834598885882110754092152266643';
	var b = '2717493709717434019846726068066227360000099460012528204257673322795';
	console.log("mut:" + this.mul(a, b));
	//除法
	var a = '2717493729240912963742712427760612109834598885882110754092152266643';
	var b = '2717493729240912963742712427760612109834598885882110754092152266643';
	console.log("div:" + this.div(a, b));
 * 
 */

class BigNumber {
    private radix: number = 10;    //进制调整
	public constructor() {}

    //去除开头的0
	private ltrimZero(str):any {
		for (var i = 0; i < str.length && str.charAt(i) === '0'; i++);
		return str.slice(i);
	}

    //大数加
	public add(a, b): any {
		a = a + '';
		b = b + '';

		if (a.indexOf('-') >= 0 && b.indexOf('-') < 0) {
			a = a.substr(1);
			return this.sub(b, a);
		} else if (a.indexOf('-') < 0 && b.indexOf('-') >= 0) {
			b = b.substr(1);
			return this.sub(a, b);
		} else if (a.indexOf('-') >= 0 && b.indexOf('-') >= 0) {
			a = a.substr(1);
			b = b.substr(1);
			return '-' + this.addWithoutSign(a, b);
		} else {
			return this.addWithoutSign(a, b);
		}
	}

	//大数无符号加
	public addWithoutSign(a, b): any {
		var lena = a.split('').length;
		var lenb = b.split('').length;

		a = a.split('').reverse();
		b = b.split('').reverse();

		var total: any = [];
		let addFlag = 0;

		//a比较长的情况(分类讨论，以后可以改进)
		if (lena >= lenb) {
			for (let i = 0, j = 0; i < lena; i++ , j++) {
				if (j < lenb) {
					total[i] = parseInt(a[i], this.radix) + parseInt(b[j], this.radix) + addFlag;
					total[i] = total[i].toString(this.radix);
				} else {
					total[i] = parseInt(a[i], this.radix) + addFlag;
					total[i] = total[i].toString(this.radix);
				}
				var tempTotal = parseInt(total[i], this.radix);
				if (tempTotal >= this.radix) {
					total[i] = (tempTotal - this.radix).toString(this.radix);
					if (i == (lena - 1)) {
						total[lena] = '1';
					}
					addFlag = 1;
				} else {
					addFlag = 0;
				}
			}
			total = total.reverse().join('');
			return total;
		} else {
			for (let i = 0, j = 0; i < lenb; i++ , j++) {
				if (j < lena) {
					total[i] = parseInt(a[j], this.radix) + parseInt(b[i], this.radix) + addFlag;
					total[i] = total[i].toString(this.radix);
				} else {
					total[i] = parseInt(b[i], this.radix) + addFlag;
					total[i] = total[i].toString(this.radix);
				}

				var tempTotal = parseInt(total[i], this.radix);
				if (tempTotal >= this.radix) {
					total[i] = (tempTotal - this.radix).toString(this.radix);
					if (i == (lenb - 1)) {
						total[lenb] = 1;
					}
					addFlag = 1;
				} else {
					addFlag = 0;
				}
			}
			total = total.reverse().join('');
			return total;
		}
	}

    //大数减
	public sub(a, b): any {
		a = a + '';
		b = b + '';

		if (a.indexOf('-') >= 0 && b.indexOf('-') < 0) {
			a = a.substr(1);
			return '-' + this.addWithoutSign(a, b);
		} else if (a.indexOf('-') < 0 && b.indexOf('-') >= 0) {
			b = b.substr(1);
			return this.addWithoutSign(a, b);
		} else if (a.indexOf('-') >= 0 && b.indexOf('-') >= 0) {
			a = a.substr(1);
			b = b.substr(1);
			return this.subWithoutSign(b, a);
		} else {
			return this.subWithoutSign(a, b);
		}
	}

	//大数无符号减
	public subWithoutSign(a, b): any {
		a = a + '';
		b = b + '';

		var result: any = [];
		var output: any = [];
		var minusOne = 0;

		a = this.ltrimZero(a);
		b = this.ltrimZero(b);

		//补0对齐
		while (a.length < b.length) {
			a = '0' + a;
		}
		while (b.length < a.length) {
			b = '0' + b;
		}
		//从后面位数向前相减
		for (var i = a.length - 1; i >= 0; i--) {
			var c1 = parseInt(a.charAt(i), this.radix) - 0;
			var c2 = parseInt(b.charAt(i), this.radix) - 0;
			//如果当前位数无须借位
			if (c1 - minusOne >= c2) {
				result.unshift(c1 - c2 - minusOne);
				output.unshift((c1 - c2 - minusOne).toString(this.radix));
				minusOne = 0;
			} else {
				result.unshift(c1 + this.radix - c2 - minusOne);
				output.unshift((c1 + this.radix - c2 - minusOne).toString(this.radix));
				minusOne = 1;
			}
		}
		//如果最高位仍然要借位
		if (minusOne) {
			var newResult = this.sub(b, a);
			newResult = this.ltrimZero(newResult);
			return '-' + newResult;
		}

		result = result.join('');
		output = output.join('');
		result = this.ltrimZero(result);
		output = this.ltrimZero(output);
		if (output == '') {
			output = '0';
		}
		return output;
	}

	//大数乘
	public mul(a, b): any {
		a = a + '';
		b = b + '';

		if (a.indexOf('-') >= 0 && b.indexOf('-') < 0) {
			a = a.substr(1);
			return '-' + this.mulWithoutSign(a, b);
		} else if (a.indexOf('-') < 0 && b.indexOf('-') >= 0) {
			b = b.substr(1);
			return '-' + this.mulWithoutSign(a, b);
		} else if (a.indexOf('-') >= 0 && b.indexOf('-') >= 0) {
			a = a.substr(1);
			b = b.substr(1);
			return this.mulWithoutSign(a, b);
		} else {
			return this.mulWithoutSign(a, b);
		}
	}

	//大数无符号乘
	public mulWithoutSign(a, b): any {
		var str1, str2, len1, len2, maxlen;
		var result: any = [];
		var output: any = [];
		str1 = a.split('').reverse();
		str2 = b.split('').reverse();
		len1 = str1.length;
		len2 = str2.length;

		for (var i = 0; i < len1; i++) {
			for (var j = 0; j <= len2; j++) {
				result[i + j] = 0;
				output[i + j] = result[i + j].toString(this.radix);
			}
		}

		for (var i = 0; i < len1; i++) {
			for (var j = 0; j < len2; j++) {
				result[i + j] += parseInt(str1[i], this.radix) * parseInt(str2[j], this.radix);
				output[i + j] = result[i + j].toString(this.radix);
			}
		}

		var n = result.length;
		for (var k = 0; k < n; k++) {
			var temp = result[k];
			if (temp >= this.radix) {
				result[k] = temp % this.radix;
				result[k + 1] += Math.floor(temp / this.radix);
				output[k] = result[k].toString(this.radix);
				output[k + 1] = result[k + 1].toString(this.radix);
			}
		}

		output = output.reverse().join('');
		output = this.ltrimZero(output);
		if (output == '') {
			output = '0';
		}
		return output
	}

	//大数除法(只支持整数除法)
	public div(a, b): any {
		a = a + '';
		b = b + '';

		if (a.indexOf('-') >= 0 && b.indexOf('-') < 0) {
			a = a.substr(1);
			return '-' + this.divWithoutSign(a, b);
		} else if (a.indexOf('-') < 0 && b.indexOf('-') >= 0) {
			b = b.substr(1);
			return '-' + this.divWithoutSign(a, b);
		} else if (a.indexOf('-') >= 0 && b.indexOf('-') >= 0) {
			a = a.substr(1);
			b = b.substr(1);
			return this.divWithoutSign(a, b);
		} else {
			return this.divWithoutSign(a, b);
		}
	}

	//无符号除法,返回商
	public divWithoutSign(a, b): any {
		a = a + '';
		b = b + '';

		if (this.compare(a, b) < 0) {
			return '0';
		} else if (this.compare(a, b) == 0) {
			return '1';
		}

		var alen = a.length;
		var blen = b.length;
		var times = alen - blen + 1;
		var result = [];

		for (var c = 0; c < times; c++) {
			var tempb = b;
			result[c] = 0;
			//补0
			while (tempb.length < alen - c) {
				tempb = tempb + '0';
			}
			while (this.compare(a, tempb) >= 0) {
				result[c]++;
				a = this.sub(a, tempb);
			}
		}

		for (var c = 0; c < result.length; c++) {
			result[c] = result[c].toString(this.radix);
		}
		var output = this.ltrimZero(result.join(''));
		return output;
	}

	//取余(a可正可负，b只能为正数),最后的结果恒为非负数 
	public mod(a, b): any {
		a = a + '';
		b = b + '';
		if (b.indexOf('-') != -1) {
			return 'b只能为正数';
		}
		// a - (a/b)*b
		var output = this.div(a, b);
		output = this.mul(output, b);
		output = this.sub(a, output);

		if (output.indexOf('-') != -1) {
			output = this.add(output, b);
		}
		return output;
	}

	/** 比较函数(1:a大，±0：相等，-1,：b大)*/
	public compare(a, b): any {
		a = a + '';
		b = b + '';

		var sign = 1;
		a = a.toString();
		b = b.toString();
		if (a.indexOf('-') >= 0 && b.indexOf('-') < 0) {
			return -1;
		} else if (a.indexOf('-') < 0 && b.indexOf('-') >= 0) {
			return 1;
		} else if (a.indexOf('-') >= 0 && b.indexOf('-') >= 0) {
			sign = -1;
			a = a.substr(1);
			b = b.substr(1);
		}

		a = a.replace(/^0+/, '');
		b = b.replace(/^0+/, '');

		var flag;

		if (a.length < b.length) {
			flag = -1;
		} else if (a.length > b.length) {
			flag = 1;
		} else {
			flag = 0;
		}

		if (flag == 0) {
			var aArr = a.split('');
			var bArr = b.split('');
			for (var i = 0; i <= aArr.length; i++) {
				if (aArr[i] > bArr[i]) {
					flag = 1;
					break;
				} else if (aArr[i] < bArr[i]) {
					flag = -1;
					break;
				}
			}
		}
		return sign * flag;
	}

	/**
     * 字符串的显示
     * @param num
     */
    /*public numOverLenght(num: any): string {
		num = num + "";
		var reg = /^[0-9]+$/;		//判断字符串是否为数字
	　　if (!reg.test(num)) { 
	　　　　return num;
	　　}

        var str = null;
        if (num.length < 5) {
            str = "" + num;
        } else if (num.length >= 5) {
			let result = [];
			this.billion(num, result);
			str = result.join("");
		}
        return str;
	}*/

	public billion(num:string, list:Array<string>, decimal:string = ""):void {
		if (num.length >= 9) {
			let mold = BigNumber.getIt().mod(num, "100000000");
			decimal = BigNumber.getIt().div(mold, "10000000");
			let value = BigNumber.getIt().div(num, "100000000");
			list.push("亿");
			this.billion(value, list, decimal);
		} else {
			this.tenthousand(num, list, decimal);
		}
	}

	public tenthousand(num:string, list:Array<string>, decimal:string):void {
		if (num.length < 5) {
			let str = num + "." + decimal;
            list.unshift(str);
        } else if (num.length >= 5) {
			let mold = BigNumber.getIt().mod(num, "10000");
			let value = BigNumber.getIt().div(mold, "1000");
			let str = BigNumber.getIt().div(num, "10000") + "." + value + "万";
			list.unshift(str);
		}
	}

	//大数随机数(生成0-n之间的随机数)
	public randomNum(n):any {
		n = n + '';
		var flag = 10000000000000000;
		var randomWithoutRadix = Math.random();
		var random = randomWithoutRadix.toString(this.radix);
		var randomOutput = random.substr(random.indexOf('.') + 1, random.length);
		var multiplyNum = this.mul(randomOutput, n);
		var finalNum = this.div(multiplyNum, flag.toString(this.radix));
		return finalNum;
	}

	private static _instance: BigNumber;
    public static getIt(): BigNumber {
        if (!this._instance)
            this._instance = new BigNumber();

        return this._instance;
    }
}
